﻿/*
Copyright (c) 2005-2008, CellAO Team

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    * Neither the name of the CellAO Team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ZoneEngine.Misc
{
	/// <summary>
	/// Class to check for collision with border lines
	/// </summary>
	public class BorderlineCheck
	{
        /// <summary>
        /// 
        /// </summary>
		private class BorderLine
		{
            /// <summary>
            /// 
            /// </summary>
			public float X1;
			/// <summary>
			/// 
			/// </summary>
            public float Y1;
			/// <summary>
			/// 
			/// </summary>
            public float X2;
			/// <summary>
			/// 
			/// </summary>
            public float Y2;
			/// <summary>
			/// 
			/// </summary>
            public int ZoneTo;
		}

		//number of units we use as fuzzy detection (ie. within +/- 5 units the server will teleport the client)
		//with precise (non-fuzzy) detection we will probably not notice the client being on that exact coordinate.
		private const int distanceThreshold = 2;

		//this holds all the known border lines of the planet, sorted by PFID
		private Dictionary<int, BorderLine> Borderlines;

        /// <summary>
        /// 
        /// </summary>
		public BorderlineCheck()
		{
			//debug only
			Borderlines = new Dictionary<int, BorderLine>();
			Borderlines.Add(545, new BorderLine { X1 = 199.9f, Y1 = 553.7f, X2 = 199.9f, Y2 = 250.1f, ZoneTo = 550 });
		}

		/// <summary>
		/// Checks if a player crosses a border line in order to initiate zoning
		/// for every CharDCMove we check against all the PF's borders to see if we are crossing one of them
		/// </summary>
		/// <param name="px">player's x coord</param>
		/// <param name="py">player's y coord</param>
		/// <returns>PFID if zoning is required, otherwise 0</returns>
		public int IsOnZoneBorder(float px, float py, int PF)
		{
			IEnumerable<KeyValuePair<int, BorderLine>> playfieldBorders = Borderlines.Where(element => element.Key == PF);
			foreach (KeyValuePair<int, BorderLine> b in playfieldBorders)
			{
				if (b.Value.X1 == b.Value.X2) //vertical line, we only check x
				{
					//check against the line length
					bool isWithinLineLength;
					if (b.Value.Y1 > b.Value.Y2)
						isWithinLineLength = (py >= b.Value.Y2 && py <= b.Value.Y1);
					else
						isWithinLineLength = (py >= b.Value.Y1 && py <= b.Value.Y2);

					if (
						px <= b.Value.X1 + distanceThreshold && 
						px >= b.Value.X1 - distanceThreshold &&
						isWithinLineLength)
						return b.Value.ZoneTo;
				}
				else if (b.Value.Y1 == b.Value.Y2 && py == b.Value.Y1) //horizontal line, only check y
				{
					if (py <= b.Value.Y1 + distanceThreshold && py >= b.Value.Y1 - distanceThreshold)
						return b.Value.ZoneTo;
				}
				else
				{
					float g = G(px, b.Value);
					//experimental, try to make a fuzzy diagonal line
					if (py <= g + distanceThreshold && py >= g - distanceThreshold)
						return b.Value.ZoneTo;
				}
			}
			return 0;
		}

		/// <summary>
		/// linear function to return the y (or z)-coordinate depending on an x coordinate
		/// if y == our original y, then we're on the line, otherwise, we're not.
		/// </summary>
		/// <param name="x">the player's x coordinate</param>
		/// <returns>the player's y coordinate if it was on the line</returns>
		private float G(float x, BorderLine line)
		{
			return (line.Y2 - line.Y1) / (line.X2 - line.X1) * x + (line.Y1 - ((line.Y2 - line.Y1) / (line.X2 - line.X1)) * line.X1);
		}
	}
}
